<?

class view_form{
    private $name, 
    	$action, 
    	$fields = array(), 
    	$removed_fields, 
    	$hidden_fields, 
    	$display_names, 
        $default_values, 
        $input_types, 
        $allowed_values, 
        $type_handlers,
        $row_type_handlers,
        $input_handlers,
        $row_handler,
        $wrap_handler,
        $only_inputs,
        $noradio,
        $form_class;
        
        
    public function __construct($name = null, $action = ""){
        if(!$name) $name = get_class($this);
        $this->name = $name;
        $this->action = $action;
        
        $this->setTypeHandler('text', function($field_name, $default_value, $allowed_values){
            return "<textarea name='$field_name' class='$field_name'>$default_value</textarea>";
		});
        
        /*$this->setTypeHandler('submit', function($field_name, $default_value, $allowed_values){
            return "<input type='submit' value='$default_value'>";
		});*/
        
        $this->setTypeHandler('enum', function($field_name, $default_value, $allowed_values){
        	$ret = ""; 
            foreach($allowed_values as $key=>$one){
                if($key == $default_value)
                    $ret .= "<input type='radio' name='$field_name' value='$key' checked='checked' id='radio_{$field_name}_$key'><label for='radio_{$field_name}_$key'>{$one['display_value']}</label> ";
                else
                    $ret .= "<input type='radio' name='$field_name' value='$key' id='radio_{$field_name}_$key'><label for='radio_{$field_name}_$key'>{$one['display_value']}</label> ";
            }
            return $ret;
		});
		
        $this->setTypeHandler('file', function($field_name, $default_value, $allowed_values){
            $max_file_size = 1000000;
            $r = "<input name='$field_name' type='file' MAX_FILE_SIZE='$max_file_size'>";
            return $r;
        });

        $this->setTypeHandler('checkbox', function($field_name, $default_value, $allowed_values){
        	$checked_str = $default_value ? "checked='checked'" : "";
            $r = "<input name='$field_name' type='checkbox' value='1' $checked_str>";
            return $r;
        });
        
        $this->setTypeHandler('password', function($field_name, $default_value, $allowed_values){
            $r = "<input name='$field_name' type='password' value='$default_value'>";
            return $r;
        });
		
        $this->setWrapHandler(function($rows, $name){
			return "<table class='formview {$name}'>{$rows}</table>";
		});
		
		$this->setRowHandler(function($name, $display_name, $input, $parity){
            $row  = "<tr class='{$name} {$parity}'>";
            $row .= "<td class='title'>{$display_name}</td><td class='input'>{$input}</td>";
            $row .= "</tr>";
			return $row;
		});
        
        $this->setRowTypeHandler("_text", function($name, $display_name, $input, $parity){
            $row  = "<tr class='{$name} {$parity}'>";
            $row .= "<td class='_text' colspan='2'>$display_name</td>";
            $row .= "</tr>";
            return $row;
        });
        
        $this->setRowTypeHandler("submit", function($name, $display_name, $input, $parity, $default_value){
            $row  = "<tr class='{$name} {$parity}'>";
            $row .= "<td class='_text' colspan='2'><input type='submit' value='$default_value' hotkey='Ctrl+Enter'></td>";
            $row .= "</tr>";
            return $row;
        });
    }
    
    public function getName(){
    	return $this->name;
	}
    
    public function createFromTable($table_name, $prefix = "", $postfix = ""){
        $fields = Database::select("SHOW FIELDS FROM $table_name");
        foreach($fields as $one) 
        	$this->addField($prefix.$one['Field'].$postfix, $one['Type'], $one['Default']);
        if(!$this->only_inputs) $this->addField("submit", "submit", "Отправить");
        $this->setDisplayName("submit", "&nbsp;");
	}
    
    public function addField($name, $type = "", $default_value = ""){
    	$this->fields[$name] = array("Type" => $type, "Default" => $default_value);
	}
	
	public function setFormClass($class_name){
		$this->form_class = $class_name;
	}
    
    public function setType($name, $type){
        if(!isset($this->fields[$name])) return;
        $this->fields[$name]['Type'] = $type;
    }
    
    public function insertAfterField($after_what, $name, $type, $default_value = ""){
        $for_insert = array("Type" => $type, "Default" => $default_value);
        $array_keys = array_keys($this->fields);
        $array_values = array_values($this->fields);
        if($after_what === false){
            $this->addField($name, $type, $default_value);
            return;
        }
        if(is_numeric($after_what)){
            $position = $after_what;
        }
        else{
            $position = array_search($after_what, $array_keys)+1;
        }
        array_splice($array_keys, $position, 0, array($name));
        array_splice($array_values, $position, 0, array($for_insert));
        $this->fields = array_combine($array_keys, $array_values);
    }
    
    public function insertText($text, $after_field = false, $name = false){
        //if($after_field === false) $after_field = count($this->fields) - 1;
        if(!$name || isset($this->fields[$name])) $name = "_text".md5(rand()); //если имя конфликтует, либо не передано, задаем его случайно
        $this->insertAfterField($after_field, $name, "_text");
        $this->setDisplayName($name, $text);
    }
	
    public function setTypeHandler($type, $callback){
        $this->type_handlers[$type] = $callback;
    }
    
    public function removeTypeHandler($type){
        unset($this->type_handlers[$type]);
    }
	
    public function setRowTypeHandler($type, $callback){
		$this->row_type_handlers[$type] = $callback;
	}
	
	public function setInputHandler($name, $callback){
		$this->input_handlers[$name] = $callback;
	}
	
	public function setRowHandler($callback){
		$this->row_handler = $callback;
	}
	
	public function setWrapHandler($callback){
		$this->wrap_handler = $callback;
	}
    
    public function getRows(){
        $i = 0;
        $rows = "";
        foreach($this->fields as $name=>$one){
            if(isset($this->removed_fields[$name])) continue;
            $parity = ++$i & 1 ? "odd" : "even";
            $input_type = $this->getInputType($name);
            if(isset($this->row_type_handlers[$input_type])){
                $row_type_handler = $this->row_type_handlers[$input_type];
                $rows .= $row_type_handler($name, $this->getDisplayName($name), $this->getInput($name), $parity, $this->getDefaultValue($name));
            }
            else{
                $row_handler = $this->row_handler;
                $rows .= $row_handler($name, $this->getDisplayName($name), $this->getInput($name), $parity);
            }
        }
        return $rows;
    }
    
    public function getHtml(){
        $r = "";
        if(!$this->only_inputs) $r .= "<form action='{$this->action}' method='post' name='{$this->name}' enctype='multipart/form-data' class='{$this->form_class}'>";
        $rows = $this->getRows();
        $wrap_handler = $this->wrap_handler;
        $r .= $wrap_handler($rows, $this->name);
        if(!empty($this->hidden_fields))
        	foreach($this->hidden_fields as $key=>$val)
            	$r .= "<input type='hidden' name='$key' value='$val' />";
        if(!$this->only_inputs) $r .= "</form>";
        return $r;
    }
    
    public function getOutput(){
        return $this->getHtml();
    }
    
    public function getInput($field_name){
        $input_type = $this->getInputType($field_name);
        $input_type = strtolower($input_type);
        $input_type = preg_replace("/\(.*\)/i", "", $input_type);
        $input_type = trim($input_type);
        $default_value = $this->getDefaultValue($field_name);
        $allowed_values = $this->getAllowedValues($field_name);
        if(isset($this->input_handlers[$field_name]))
        	return $this->input_handlers[$field_name]($field_name, $default_value, $allowed_values);
        	
        if(isset($this->type_handlers[$input_type]))
        	return $this->type_handlers[$input_type]($field_name, $default_value, $allowed_values);
        
        if($allowed_values){
            $r = "<select name='$field_name'>";
            foreach($allowed_values as $key=>$val){
                $selected_str = ""; $css_class_str = "";
                if($key == $this->getDefaultValue($field_name)) $selected_str = " selected='selected'";
                if($val['css_class']) $css_class_str = " class='{$val['css_class']}'";
                $r .= "<option value='$key'{$selected_str}{$css_class_str}>{$val['display_value']}</option>";
            }
            $r .= "</select>";
            return $r;
        }
        
        return "<input type='text' name='$field_name' class='$field_name' value='$default_value'>";
    }
    
    public function onlyInputs(){$this->only_inputs = true;}
    public function noRadio(){$this->noradio = true;}
    
    public function getInputType($field_name){
        if(isset($this->input_types[$field_name])) return $this->input_types[$field_name];
        return $this->fields[$field_name]['Type'];
    }
    
    public function setInputType($field_name, $input_type){
        $this->input_types[$field_name] = $input_type;
    }
    
    public function getDisplayName($field_name){
        if(!empty($this->display_names[$field_name]))
            return $this->display_names[$field_name];
        return $field_name;
    }
    
    public function setDisplayName($field_name, $display_name){
        $this->display_names[$field_name] = $display_name;
    }
    
    public function getDefaultValue($field_name){
        if(isset($this->default_values[$field_name])) return $this->default_values[$field_name];
        return $this->fields[$field_name]['Default'];
    }
    
    public function setDefaultValue($field_name, $value){
        $this->default_values[$field_name] = $value;
    }
    
    public function loadFromArray($array){
        foreach($array as $key=>$val) $this->setDefaultValue($key, $val);
    }
    
    public function loadFromSession(){
    	if(empty($_SESSION[$this->getName()])) return;
    	if(!is_array($_SESSION[$this->getName()])) return;
    	$this->loadFromArray($_SESSION[$this->getName()]);
	}
    
    public function removeField($field_name){
        $this->removed_fields[$field_name] = true;
    }
    
    public function keepFields(/*....*/){
        $args = func_get_args();
        foreach($this->fields as $key=>$one){
            if(in_array($key, $args)) continue;
            $this->removeField($key);
        }
    }
    
    public function setFieldPosition($field_name, $position){
        if(!isset($this->fields[$field_name])) return;
        $finfo = $this->fields[$field_name];
        unset($this->fields[$field_name]);
        $this->insertAfterField($position, $field_name, $finfo['Type'], $finfo['Default']);
    }
    
    public function setSubmitValue($value){
        $this->setDefaultValue('submit', $value);	
    }
    
    public function addAllowedValue($field_name, $value, $display_value = false, $css_class = false){
        if($display_value === false) $display_value = $value;
        $this->allowed_values[$field_name][$value] = array('display_value' => $display_value, 'css_class' => $css_class);
    }
    
    public function addHiddenField($name, $value){
        $this->hidden_fields[$name] = $value;
    }
    
    public function hideFields($fields){
        if(!is_array($fields)) $fields = func_get_args();
        foreach($fields as $one){
        	$def_val = $this->getDefaultValue($one);
            $this->removeField($one);
            $this->addHiddenField($one, $def_val);
        }
	}

    public function hideAllExceptFields($fields){
        if(!is_array($fields)) $fields = func_get_args();
        foreach($this->fields as $key=>$one){
            if(in_array($key, $fields)) continue;
            $this->hideFields($key);
        }
    }
	
	
    public function getAllowedValues($field_name){
        if(!empty($this->allowed_values[$field_name]))
        	return $this->allowed_values[$field_name];
		$input_type = $this->getInputType($field_name);
        if(stripos($input_type, 'enum') === 0){
        	preg_match("/\((.+)\)/i", $input_type, $m);
        	if(empty($m[1])) return array();
        	$enum_list = explode(',', $m[1]);
        	$ret = array();
        	foreach($enum_list as $key=>$val){ 
        		$pure = trim($val, '\'" ');
        		$ret[$pure] = array("display_value" => $pure);
			}
        	return $ret;
		}
    }
}
?>